using System;
using System.Collections.Generic;
using System.Net;
using System.Timers;
using SpoonStun.Message;

namespace SpoonStun.Handler
{
	internal class StunCoreHandler
	{
		private StunHandler _handler;
		private List<IPEndPoint> _clientList = new List<IPEndPoint>();

		private Timer _pingTimer = new Timer(1000);
		private SortedList<int, IPEndPoint> _pingList = new SortedList<int, IPEndPoint>();

		private SortedList<int, IPEndPoint> _peerList = new SortedList<int, IPEndPoint>();

		public StunCoreHandler(StunHandler handler)
		{
			_handler = handler;

			_pingTimer.Elapsed += new ElapsedEventHandler(_pingTimer_Elapsed);
			_pingTimer.Start();

			handler.RegisterHandler(new OnReceiveMessageHandler<RegisterMessage>(RegisterReceive));
			handler.RegisterHandler(new OnReceiveMessageHandler<PingMessage>(PingReceive));
			handler.RegisterHandler(new OnReceiveMessageHandler<PeerMessage>(PeerReceive));
		}

		private void PeerReceive(StunHandler handler, IPEndPoint client, PeerMessage msg)
		{
			if (msg.Type == PeerMessage.TypeValue.Request)
			{
				if (_clientList.Contains(client))
				{
					// From client
					if (_clientList.Contains(msg.PeerEndPoint))
					{
						Console.WriteLine("Peerrequest for " + msg.PeerEndPoint + ", " + msg.Id);
						msg.PeerSenderEndPoint = client;
						_handler.Send(msg.PeerEndPoint, msg);
					}
					else Console.WriteLine("Peer not found: " + msg.PeerEndPoint);
				}
				else
				{
					// From server
					if (_handler.Server.Servers.Contains(client))
					{
						Console.WriteLine("Peerrequest for " + msg.PeerEndPoint + ", " + msg.Id);
						PeerMessage res = new PeerMessage();
						res.Type = PeerMessage.TypeValue.Test;
						res.Id = msg.Id;
						_handler.Send(msg.PeerSenderEndPoint, res);
					}
					else Console.WriteLine("Sender not in list");
				}
			}
			if (msg.Type == PeerMessage.TypeValue.Test)
			{
				if (_peerList.ContainsKey(msg.Id))
				{
					Console.WriteLine("Peer test message from: " + client);
				}
			}
		}

		private void _pingTimer_Elapsed(object sender, ElapsedEventArgs e)
		{
			foreach (IPEndPoint point in _clientList)
			{
				SendPing(point);
			}
		}

		private void SendPing(IPEndPoint point)
		{
			PingMessage msg = new PingMessage();
			_pingList.Add(msg.Id, point);
			_handler.Send(point, msg);
			//Console.WriteLine("Send Ping to " + point);
		}

		private void PingReceive(StunHandler handler, IPEndPoint client, PingMessage msg)
		{
			if (msg.Type == PingMessage.TypeValue.Request)
			{
				PingMessage res = new PingMessage();
				res.Id = msg.Id;
				res.Type = PingMessage.TypeValue.Response;
				handler.Send(client, res);
			}
			if (msg.Type == PingMessage.TypeValue.Response)
			{
				if (_pingList.ContainsKey(msg.Id)) _pingList.Remove(msg.Id);
			}
		}

		private void RegisterReceive(StunHandler handler, IPEndPoint client, RegisterMessage msg)
		{
			if (msg.Type == RegisterMessage.TypeValue.Request)
			{
				_clientList.Add(client);
				Console.WriteLine("Register endpoint: " + client);

				RegisterMessage res = new RegisterMessage();
				res.Type = RegisterMessage.TypeValue.Response;
				res.Id = msg.Id;
				res.GlobalEndPoint = client;
				handler.Send(client, res);
			}
			if (msg.Type == RegisterMessage.TypeValue.Response)
			{
				handler.Server.AddGlobalEndPoint(msg.GlobalEndPoint);
				Console.WriteLine("Register complete: " + msg.GlobalEndPoint);
			}
		}

		public void PeerWith(List<IPEndPoint> endPoints)
		{
			foreach (IPEndPoint point in endPoints)
			{
				// Open port
				PeerMessage tmp = new PeerMessage();
				tmp.Type = PeerMessage.TypeValue.Test;
				_handler.Send(point, tmp);

				PeerMessage msg = new PeerMessage();
				msg.PeerEndPoint = point;
				Console.WriteLine("Send peer request: " + msg.Id);
				_peerList.Add(msg.Id, point);
				foreach (IPEndPoint client in _handler.Server.Servers)
				{
					_handler.Send(client, msg);
				}
			}
		}
	}
}